import json
import os
import time
import pyautogui
import pyperclip
from datetime import datetime
import uuid

class MacroItem:
    """宏命令基类"""
    def __init__(self, item_type):
        self.id = str(uuid.uuid4())
        self.type = item_type
        self.created_at = datetime.now().isoformat()
        
    def get_display_text(self):
        """获取显示文本"""
        return f"{self.type} 命令"
        
    def execute(self, log_callback=None, stop_event=None):
        """执行宏命令"""
        if log_callback:
            log_callback(f"执行 {self.get_display_text()}")
        return True
        
    def to_dict(self):
        """转换为字典"""
        return {
            "id": self.id,
            "type": self.type,
            "created_at": self.created_at
        }
        
    @classmethod
    def from_dict(cls, data):
        """从字典创建对象"""
        item = cls()
        item.id = data.get("id", str(uuid.uuid4()))
        item.created_at = data.get("created_at", datetime.now().isoformat())
        return item


class KeyboardMacro(MacroItem):
    """键盘操作宏命令"""
    def __init__(self, key=None, action_type=None):
        super().__init__("keyboard")
        self.key = key or "enter"
        self.action_type = action_type or "Click"
        
    def get_display_text(self):
        return f"键盘: {self.key} {self.action_type}"
        
    def execute(self, log_callback=None, stop_event=None):
        try:
            if stop_event and stop_event.is_set():
                return False
            if self.action_type == "Click":
                pyautogui.press(self.key)
            elif self.action_type == "Press":
                pyautogui.keyDown(self.key)
            elif self.action_type == "Release":
                pyautogui.keyUp(self.key)
                
            if log_callback:
                log_callback(f"执行键盘操作: {self.key} {self.action_type}")
            return True
        except Exception as e:
            if log_callback:
                log_callback(f"键盘操作失败: {str(e)}", is_error=True)
            return False
            
    def to_dict(self):
        data = super().to_dict()
        data.update({
            "key": self.key,
            "action_type": self.action_type
        })
        return data
        
    @classmethod
    def from_dict(cls, data):
        item = cls()
        item.id = data.get("id", str(uuid.uuid4()))
        item.created_at = data.get("created_at", datetime.now().isoformat())
        item.key = data.get("key", "enter")
        item.action_type = data.get("action_type", "Click")
        return item


class TextMacro(MacroItem):
    """文本输入宏命令"""
    def __init__(self, content=None):
        super().__init__("text")
        self.content = content or ""
        
    def get_display_text(self):
        return f"输入文本: {self.content[:20]}{'...' if len(self.content) > 20 else ''}"
        
    def execute(self, log_callback=None, stop_event=None):
        try:
            if stop_event and stop_event.is_set():
                return False
            pyperclip.copy(self.content)
            pyautogui.hotkey("ctrl", "v")
            if log_callback:
                log_callback(f"输入文本: {self.content}")
            return True
        except Exception as e:
            if log_callback:
                log_callback(f"文本输入失败: {str(e)}", is_error=True)
            return False
            
    def to_dict(self):
        data = super().to_dict()
        data.update({
            "content": self.content
        })
        return data
        
    @classmethod
    def from_dict(cls, data):
        item = cls()
        item.id = data.get("id", str(uuid.uuid4()))
        item.created_at = data.get("created_at", datetime.now().isoformat())
        item.content = data.get("content", "")
        return item


class MouseMoveMacro(MacroItem):
    """鼠标移动宏命令"""
    def __init__(self, x=None, y=None):
        super().__init__("mouse_move")
        self.x = x or 0
        self.y = y or 0
        
    def get_display_text(self):
        return f"鼠标移动到: ({self.x}, {self.y})"
        
    def execute(self, log_callback=None, stop_event=None):
        try:
            pyautogui.moveTo(self.x, self.y)
            if log_callback:
                log_callback(f"鼠标移动到: ({self.x}, {self.y})")
            return True
        except Exception as e:
            if log_callback:
                log_callback(f"鼠标移动失败: {str(e)}", is_error=True)
            return False
            
    def to_dict(self):
        data = super().to_dict()
        data.update({
            "x": self.x,
            "y": self.y
        })
        return data
        
    @classmethod
    def from_dict(cls, data):
        item = cls()
        item.id = data.get("id", str(uuid.uuid4()))
        item.created_at = data.get("created_at", datetime.now().isoformat())
        item.x = data.get("x", 0)
        item.y = data.get("y", 0)
        return item


class MouseMoveToImageMacro(MacroItem):
    """鼠标移动到图片位置宏命令"""
    def __init__(self, src=None, grayscale=None, confidence=None):
        super().__init__("mouse_move_image")
        self.src = src or ""
        self.grayscale = grayscale if grayscale is not None else True
        self.confidence = confidence or 0.8
        
    def get_display_text(self):
        return f"移动到图片: {os.path.basename(self.src)}"
        
    def execute(self, log_callback=None, stop_event=None):
        try:
            if stop_event and stop_event.is_set():
                return False
            if not os.path.exists(self.src):
                if log_callback:
                    log_callback(f"图片文件不存在: {self.src}", is_error=True)
                return False
                
            # 查找图片位置
            position = pyautogui.locateOnScreen(
                self.src, 
                grayscale=self.grayscale,
                confidence=self.confidence
            )
            
            if position:
                # 移动到图片中心
                x, y, width, height = position
                center_x, center_y = x + width/2, y + height/2
                pyautogui.moveTo(center_x, center_y)
                
                if log_callback:
                    log_callback(f"移动到图片位置: ({center_x}, {center_y})")
                return True
            else:
                if log_callback:
                    log_callback(f"未找到图片: {os.path.basename(self.src)}", is_error=True)
                return False
                
        except Exception as e:
            if log_callback:
                log_callback(f"移动到图片失败: {str(e)}", is_error=True)
            return False
            
    def to_dict(self):
        data = super().to_dict()
        data.update({
            "src": self.src,
            "grayscale": self.grayscale,
            "confidence": self.confidence
        })
        return data
        
    @classmethod
    def from_dict(cls, data):
        item = cls()
        item.id = data.get("id", str(uuid.uuid4()))
        item.created_at = data.get("created_at", datetime.now().isoformat())
        item.src = data.get("src", "")
        item.grayscale = data.get("grayscale", True)
        item.confidence = data.get("confidence", 0.8)
        return item


class MouseMacro(MacroItem):
    """鼠标操作宏命令"""
    def __init__(self, button=None, action_type=None):
        super().__init__("mouse")
        self.button = button or "left"
        self.action_type = action_type or "Click"
        
    def get_display_text(self):
        return f"鼠标{self.button}键: {self.action_type}"
        
    def execute(self, log_callback=None, stop_event=None):
        try:
            if stop_event and stop_event.is_set():
                return False
            if self.action_type == "Click":
                pyautogui.click(button=self.button)
            elif self.action_type == "Press":
                pyautogui.mouseDown(button=self.button)
            elif self.action_type == "Release":
                pyautogui.mouseUp(button=self.button)
                
            if log_callback:
                log_callback(f"执行鼠标操作: {self.button}键 {self.action_type}")
            return True
        except Exception as e:
            if log_callback:
                log_callback(f"鼠标操作失败: {str(e)}", is_error=True)
            return False
            
    def to_dict(self):
        data = super().to_dict()
        data.update({
            "button": self.button,
            "action_type": self.action_type
        })
        return data
        
    @classmethod
    def from_dict(cls, data):
        item = cls()
        item.id = data.get("id", str(uuid.uuid4()))
        item.created_at = data.get("created_at", datetime.now().isoformat())
        item.button = data.get("button", "left")
        item.action_type = data.get("action_type", "Click")
        return item


class ScrollMacro(MacroItem):
    """滚轮滚动宏命令"""
    def __init__(self, amount=None):
        super().__init__("scroll")
        self.amount = amount or 0
        
    def get_display_text(self):
        direction = "下滚" if self.amount > 0 else "上滚"
        return f"滚轮{direction}: {abs(self.amount)}"
        
    def execute(self, log_callback=None, stop_event=None):
        try:
            if stop_event and stop_event.is_set():
                return False
            pyautogui.scroll(-self.amount)
            direction = "下滚" if self.amount > 0 else "上滚"
            if log_callback:
                log_callback(f"滚轮{direction}: {abs(self.amount)}")
            return True
        except Exception as e:
            if log_callback:
                log_callback(f"滚轮操作失败: {str(e)}", is_error=True)
            return False
            
    def to_dict(self):
        data = super().to_dict()
        data.update({
            "amount": self.amount
        })
        return data
        
    @classmethod
    def from_dict(cls, data):
        item = cls()
        item.id = data.get("id", str(uuid.uuid4()))
        item.created_at = data.get("created_at", datetime.now().isoformat())
        item.amount = data.get("amount", 0)
        return item


class WaitMacro(MacroItem):
    """等待宏命令"""
    def __init__(self, milliseconds=None):
        super().__init__("wait")
        self.milliseconds = milliseconds or 1000
        
    def get_display_text(self):
        return f"等待: {self.milliseconds}毫秒"
        
    def execute(self, log_callback=None, stop_event=None):
        try:
            if log_callback:
                log_callback(f"等待 {self.milliseconds} 毫秒")
                
            # 支持中断的等待
            start_time = time.time()
            elapsed = 0
            interval = 0.1  # 100ms检查一次
            
            while elapsed < self.milliseconds / 1000:
                if stop_event and stop_event.is_set():
                    if log_callback:
                        log_callback("等待被中断")
                    return False
                    
                time.sleep(interval)
                elapsed = time.time() - start_time
                
            return True
        except Exception as e:
            if log_callback:
                log_callback(f"等待操作失败: {str(e)}", is_error=True)
            return False
            
    def to_dict(self):
        data = super().to_dict()
        data.update({
            "milliseconds": self.milliseconds
        })
        return data
        
    @classmethod
    def from_dict(cls, data):
        item = cls()
        item.id = data.get("id", str(uuid.uuid4()))
        item.created_at = data.get("created_at", datetime.now().isoformat())
        item.milliseconds = data.get("milliseconds", 1000)
        return item


class WaitKeyMacro(MacroItem):
    """等待按键宏命令"""
    def __init__(self, key=None):
        super().__init__("wait_key")
        self.key = key or "enter"
        
    def get_display_text(self):
        return f"等待按键: {self.key}"
        
    def execute(self, log_callback=None, stop_event=None):
        try:
            if log_callback:
                log_callback(f"等待按键: {self.key}")
                
            # 等待指定按键
            import keyboard
            
            while True:
                if stop_event and stop_event.is_set():
                    if log_callback:
                        log_callback("等待按键被中断")
                    return False
                    
                if keyboard.is_pressed(self.key):
                    if log_callback:
                        log_callback(f"检测到按键: {self.key}")
                    return True
                    
                time.sleep(0.1)
                
        except Exception as e:
            if log_callback:
                log_callback(f"等待按键失败: {str(e)}", is_error=True)
            return False
            
    def to_dict(self):
        data = super().to_dict()
        data.update({
            "key": self.key
        })
        return data
        
    @classmethod
    def from_dict(cls, data):
        item = cls()
        item.id = data.get("id", str(uuid.uuid4()))
        item.created_at = data.get("created_at", datetime.now().isoformat())
        item.key = data.get("key", "enter")
        return item


class MacroProject:
    """宏工程类，管理一组宏命令"""
    def __init__(self, name=None):
        self.name = name or "未命名宏工程"
        self.items = []
        self.created_at = datetime.now().isoformat()
        self.modified_at = datetime.now().isoformat()
        self.file_path = None
        
    def add_item(self, item):
        """添加宏命令"""
        self.items.append(item)
        self.modified_at = datetime.now().isoformat()
        
    def remove_item(self, item_id):
        """移除宏命令"""
        self.items = [item for item in self.items if item.id != item_id]
        self.modified_at = datetime.now().isoformat()
        
    def move_item(self, from_index, to_index):
        """移动宏命令位置"""
        if 0 <= from_index < len(self.items) and 0 <= to_index < len(self.items):
            item = self.items.pop(from_index)
            self.items.insert(to_index, item)
            self.modified_at = datetime.now().isoformat()
            
    def to_dict(self):
        """转换为字典"""
        return {
            "name": self.name,
            "created_at": self.created_at,
            "modified_at": self.modified_at,
            "items": [item.to_dict() for item in self.items]
        }
        
    def save(self, file_path=None):
        """保存宏工程到文件"""
        try:
            if file_path:
                self.file_path = file_path
                
            if not self.file_path:
                return False
                
            with open(self.file_path, 'w', encoding='utf-8') as f:
                json.dump(self.to_dict(), f, ensure_ascii=False, indent=2)
                
            self.modified_at = datetime.now().isoformat()
            return True
        except Exception as e:
            print(f"保存宏工程失败: {str(e)}")
            return False
            
    @classmethod
    def load(cls, file_path):
        """从文件加载宏工程"""
        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                data = json.load(f)
                
            project = cls()
            project.name = data.get("name", "未命名宏工程")
            project.created_at = data.get("created_at", datetime.now().isoformat())
            project.modified_at = data.get("modified_at", datetime.now().isoformat())
            project.file_path = file_path
            
            # 加载宏命令
            item_classes = {
                "keyboard": KeyboardMacro,
                "text": TextMacro,
                "mouse_move": MouseMoveMacro,
                "mouse_move_image": MouseMoveToImageMacro,
                "mouse": MouseMacro,
                "scroll": ScrollMacro,
                "wait": WaitMacro,
                "wait_key": WaitKeyMacro
            }
            
            for item_data in data.get("items", []):
                item_type = item_data.get("type")
                item_class = item_classes.get(item_type)
                
                if item_class:
                    project.add_item(item_class.from_dict(item_data))
            
            return project
        except Exception as e:
            print(f"加载宏工程失败: {str(e)}")
            return None
